<?php
/**
 * Created by PhpStorm.
 * User: alexlz
 * Date: 10/2/14
 * Time: 9:56 AM
 */

namespace base;


use log\Logger;
use sql\SimpleSQLQuery;

if (!defined('NYMPH_ROOT')) {
  define('NYMPH_ROOT', dirname("../../../" . __FILE__));
  require(NYMPH_ROOT . '/local/loader/kernel/classes/URLClassLoader.php');
}

/**
 * The Connection class provides
 * methods for executing different
 * actions with connections and it's
 * properties
 */
class Connection
{

  /**
   * @var Connection
   */
  private static $connection;

  /**
   * @var ITransaction transaction manager
   */
  private $transaction;

  /**
   * @var Logger
   */
  private $logger;

  /**
   * @var ConnectionProperties
   */
  private $connectionProperty;

  /**
   * @var boolean
   */
  private $connected;

  /**
   * @var IDBActions
   */
  private $dbActions;

  /**
   * (PHP 5)<br/>
   * @param ITransaction $transaction
   * @param ConnectionProperties $connectionProperty
   * @param IDBActions $dbActions
   * @param $logger
   * @internal param ITransaction $transaction
   * @internal param ConnectionProperties $connectionProperty
   * @internal param IDBActions $dbActions
   * @var Logger $logger
   */
  function __construct($transaction, $connectionProperty, $dbActions, $logger)
  {
    $this->transaction = $transaction;
    $this->logger = $logger;
    $this->connectionProperty = $connectionProperty;
    $this->dbActions = $dbActions;
    self::$connection = $this;
  }

  /**
   * (PHP 5)<br/>
   * Connects to database if no connection exists
   */
  public function connectDB()
  {
    if (!$this->isConnected()) {
      $this->logger->debug(DBMessages::DB_CONNECT);
      if ($this->dbActions->connectToDB($this->connectionProperty->getDbURL(), $this->connectionProperty->getDbName(), $this->connectionProperty->getUsername(), $this->connectionProperty->getPassword())) {
        $this->logger->debug(DBMessages::DB_CONNECT_SUCCEED);
      } else {
        $this->logger->error($this->dbActions->getQueryError());
      }
    }
  }

  /**
   * @return boolean
   */
  public function isConnected()
  {
    return $this->connected;
  }

  /**
   * @param boolean $connected
   */
  public function setConnected($connected)
  {
    $this->connected = $connected;
  }

  /**
   * @return ITransaction
   */
  public function getTransaction()
  {
    return $this->transaction;
  }

  /**
   * @param ITransaction $transaction
   */
  public function setTransaction($transaction)
  {
    $this->transaction = $transaction;
  }

  /**
   * @return Logger
   */
  public function getLogger()
  {
    return $this->logger;
  }

  /**
   * @param Logger $logger
   */
  public function setLogger($logger)
  {
    $this->logger = $logger;
  }

  /**
   * @return ConnectionProperties
   */
  public function getConnectionProperty()
  {
    return $this->connectionProperty;
  }

  /**
   * @param ConnectionProperties $connectionProperty
   */
  public function setConnectionProperty($connectionProperty)
  {
    $this->connectionProperty = $connectionProperty;
  }

  /**
   * (PHP 5)<br/>
   * Execute sql query
   * @param SQLQuery $sqlQuery
   * @return mixed query result or false if query failed
   */
  public function executeQuery($sqlQuery)
  {
    $this->connectDB();

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery->getQueryBody());

    if (!($result = $this->dbActions->executeStringQuery($sqlQuery->getQueryBody()))) {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    } else {
      return $result;
    }
  }

  /**
   * (PHP 5)<br/>
   * Execute sql query
   * @param string $sqlQuery
   * @return mixed query result or false if query failed
   */
  public function executeStringQuery($sqlQuery)
  {
    $this->connectDB();

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery);

    if (!($result = $this->dbActions->executeStringQuery($sqlQuery))) {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    } else {
      return $result;
    }
  }

  /**
   * (PHP 5)<br/>
   * Execute sql query in transaction
   * @param SQLQuery $sqlQuery
   * @return mixed query result or false if query failed
   */
  public function executeQueryInTransaction($sqlQuery)
  {
    $this->connectDB();

    if (!$this->transaction->startTransaction()) {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    } else {
      $this->logger->debug(DBMessages::TRANSACTION_STARTED);
    }

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery->getQueryBody());

    if (!($result = $this->dbActions->executeStringQuery($sqlQuery->getQueryBody()))) {
      $this->logger->error($this->dbActions->getQueryError());
      $this->transaction->rollbackTransaction();
      $this->logger->debug(DBMessages::TRANSACTION_ROLLED_BACK);
      return false;
    } else {
      if ($this->transaction->commitTransaction()) {
        $this->logger->debug(DBMessages::TRANSACTION_COMMITTED);
        return $result;
      } else {
        $this->logger->error($this->dbActions->getQueryError());
        return false;
      }
    }
  }

  /**
   * (PHP 5)<br/>
   * Execute sql query in transaction
   * @param string $sqlQuery
   * @return mixed query result or false if query failed
   */
  public function executeStringQueryInTransaction($sqlQuery)
  {
    $this->connectDB();

    if (!$this->transaction->startTransaction()) {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    } else {
      $this->logger->debug(DBMessages::TRANSACTION_STARTED);
    }

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery);

    if (!($result = $this->dbActions->executeStringQuery($sqlQuery))) {
      $this->logger->error($this->dbActions->getQueryError());
      $this->transaction->rollbackTransaction();
      $this->logger->debug(DBMessages::TRANSACTION_ROLLED_BACK);
      return false;
    } else {
      if ($this->transaction->commitTransaction()) {
        $this->logger->debug(DBMessages::TRANSACTION_COMMITTED);
        return $result;
      } else {
        $this->logger->error($this->dbActions->getQueryError());
        return false;
      }
    }
  }

  /**
   * (PHP 5)<br/>
   * Execute sql query and execute callback
   * @param SimpleSQLQuery $sqlQuery
   * @param ICallbackInner $innerCallback
   * @param ICallbackBefore $beforeCallback [optional]
   * @param ICallbackAfter $afterCallback [optional]
   * @return mixed query result or false if query failed
   */
  public function executeQueryWithCallback($sqlQuery, $innerCallback, $beforeCallback, $afterCallback)
  {
    $this->connectDB();

    $result = null;
    if ($beforeCallback) {
      if ($beforeCallback instanceof ICallbackBefore) {
        $beforeCallback->execute();
      } else {
        $this->logger->error("The \$beforeCallback param is not instance of ICallbackBefore interface");
      }
    } else {
      $this->logger->info("\$beforeCallback param is null. Executing query without beforeCallback");
    }

    if ($this->transaction->startTransaction()) {
      $this->logger->debug(DBMessages::TRANSACTION_STARTED);
    } else {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    }

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery->getQueryBody());

    if (!$this->dbActions->executeStringQuery($sqlQuery->getQueryBody())) {
      $this->logger->error($this->dbActions->getQueryError());
      $this->transaction->rollbackTransaction();
      $this->logger->debug(DBMessages::TRANSACTION_ROLLED_BACK);
      return false;
    } else {
      if ($innerCallback instanceof ICallbackInner) {
        $result = $innerCallback->execute();
      } else {
        $this->logger->error("The \$innerCallback param is not instance of ICallbackInner interface");
      }
      if ($this->transaction->commitTransaction()) {
        $this->logger->debug(DBMessages::TRANSACTION_COMMITTED);
      } else {
        $this->logger->error($this->dbActions->getQueryError());
      }
    }

    if ($afterCallback) {
      if ($afterCallback instanceof ICallbackAfter) {
        $afterCallback->execute();
      } else {
        $this->logger->error("The \$afterCallback param is not instance of ICallbackAfter interface");
      }
    } else {
      $this->logger->info("\$afterCallback param is null. Executing query without afterCallback");
    }
    return $result;
  }


  /**
   * (PHP 5)<br/>
   * Execute sql query and execute callback
   * @param SimpleSQLQuery $sqlQuery
   * @return boolean false if query failed
   */
  public function executeQueryUpdateInTransaction($sqlQuery)
  {
    $this->connectDB();

    if (!$this->transaction->startTransaction()) {
      $this->logger->error($this->dbActions->getQueryError());
      return false;
    } else {
      $this->logger->debug(DBMessages::TRANSACTION_STARTED);
    }

    $this->logger->debug(DBMessages::QUERY_EXECUTE . $sqlQuery->getQueryBody());

    if (!($result = $this->dbActions->executeStringQuery($sqlQuery->getQueryBody()))) {
      $this->logger->error($this->dbActions->getQueryError());
      $this->transaction->rollbackTransaction();
      $this->logger->debug(DBMessages::TRANSACTION_ROLLED_BACK);
      return false;
    } else {
      if ($this->transaction->commitTransaction()) {
        $this->logger->debug(DBMessages::TRANSACTION_COMMITTED);
      } else {
        $this->logger->error($this->dbActions->getQueryError());
        return false;
      }
    }
  }

  /**
   * @return IDBActions
   */
  public function getDbActions()
  {
    return $this->dbActions;
  }

  /**
   * @param IDBActions $dbActions
   */
  public function setDbActions($dbActions)
  {
    $this->dbActions = $dbActions;
  }

  /**
   * @return Connection
   */
  public static function getConnection()
  {
    return self::$connection;
  }

} 